package cn.you.GenghisKhan.db.relation.mybatis;

import cn.you.GenghisKhan.common.spring.SpringContextHolder;
import cn.you.GenghisKhan.config.db.DataSourceConfig;
import cn.you.GenghisKhan.db.relation.AdvisorDaoSupport;
import cn.you.GenghisKhan.db.relation.DynamicDataSource;
import cn.you.GenghisKhan.db.relation.DynamicDataSourceHolder;
import cn.you.GenghisKhan.db.relation.bean.LookUpType;
import cn.you.GenghisKhan.db.relation.bean.TenantBean;
import com.alibaba.dubbo.config.ConsumerConfig;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.ConnectionHolder;

import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.List;

public class Mybatis implements ApplicationContextAware, InitializingBean {
    private transient ApplicationContext applicationContext;
    private boolean isdefault=false;
    IDataSource iDataSource;
    AdvisorDaoSupport advisorDaoSupport;//当前数据源
    DynamicDataSource dynamicDataSource;//路由数据源 ;
    private static final Logger logger = LoggerFactory.getLogger(Mybatis.class);

    /**
     * 设置默认dataSource,当路由表中找不到其他数据源时用这个
     * 一般用于单数据库实例应用，或者做开发测试
     *
     * @param dataSource
     */
    public void setdefaultDataSource (DataSource dataSource) {
        if (dynamicDataSource == null) {
            dynamicDataSource = new DynamicDataSource();
        }
        isdefault=true;
        this.dynamicDataSource.setResolvedDefaultDataSource(dataSource);
    }

    /**
     * 允许用户自定义动态datasource
     *
     * @param iDataSource
     */
    public void setiDataSource (IDataSource iDataSource) {
        this.iDataSource = iDataSource;
    }

    public Mapper mapper () {

        Mapper dao = new Mapper(advisorDaoSupport);
        return dao;
    }

    public BizMapper mapper (String daoName) {
        BizMapper dao = new BizMapper(advisorDaoSupport, daoName);
        return dao;
    }

    public DS Get (Object tenantId) {
        if (isvalid(tenantId, null)) {
            return new DS(advisorDaoSupport);
        }
        return null;
    }

    /**
     * 根据类型读取数据源
     *
     * @return
     */
    public DS Get (LookUpType type) {
        if (setDataSource(type) != null) {
            AdvisorDaoSupport advisorDaoSupport = SpringContextHolder.getBean(SqlSessionDaoSupport.class);
            return new DS(advisorDaoSupport);
        }
        return null;
    }

    /**
     * 根据类型和数据库ID读取数据源
     *
     * @return
     */
    public DS Get (LookUpType type, Object tenantId) {
        if (setDataSource(type, tenantId) != null) {
            AdvisorDaoSupport advisorDaoSupport = SpringContextHolder.getBean(SqlSessionDaoSupport.class);
            return new DS(advisorDaoSupport);
        }
        return null;
    }


    private boolean isvalid (Object tenantId, LookUpType type) {

        if (isdefault) {
            DynamicDataSourceHolder.putDataSource(null);//读取默认数据源
        } else if (dynamicDataSource.getResolvedDataSources().get(tenantId) == null) {
            if (iDataSource != null) {
                DataSource dataSource = iDataSource.getDataSource(type, tenantId);
                dynamicDataSource.getResolvedDataSources().put(tenantId, dataSource);
                DynamicDataSourceHolder.putDataSource((String) tenantId);
            } else {
                String tag = null;
                if (type != null && tenantId != null) {
                    tag = setDataSource(type, tenantId);
                } else if (type != null) {
                    tag = setDataSource(type);
                } else if (tenantId != null) {
                    tag = setDataSource(tenantId);
                }
                if (tag != null) {



                    return true;
                }
            }
        } else {
            DynamicDataSourceHolder.putDataSource((String) tenantId);
        }
        return true;
    }

    private String setDataSource (LookUpType type, Object tenantId) {
        String dbTag = null;
        List<TenantBean> tenantBeans = DataSourceConfig.TENANTBEAN;
        for (TenantBean tenantBean : tenantBeans) {
            if (tenantBean.getType() == type) {
                //先判断指定id数据源
                List<Long> idList = tenantBean.getSpecifyList();
                if (null != idList) {
                    for (Long id : idList) {
                        if (id == Long.parseLong(tenantId.toString())) {
                            dbTag = tenantBean.getDbTag();
                            DynamicDataSourceHolder.putDataSource(dbTag);
                            return dbTag;
                        }
                    }
                }
                //后判断区间id数据源
                if (Long.parseLong(tenantId.toString()) >= tenantBean.getStart() &&
                        Long.parseLong(tenantId.toString()) <= tenantBean.getEnd()) {
                    dbTag = tenantBean.getDbTag();
                    DynamicDataSourceHolder.putDataSource(dbTag);
                    break;
                }
            }
        }
        if (dbTag == null) {
            logger.error("数据库数据源无效!LookUpType:{},key:{}，请检查配置！", type, tenantId);
        }
        return dbTag;
    }

    private String setDataSource (LookUpType type) {
        String dbTag = null;
        List<TenantBean> tenantBeans = DataSourceConfig.TENANTBEAN;
        for (TenantBean tenantBean : tenantBeans) {
            if (tenantBean.getType() == type) {
                dbTag = tenantBean.getDbTag();
                DynamicDataSourceHolder.putDataSource(dbTag);
                break;
            }
        }
        if (dbTag == null) {
            logger.error("数据库数据源无效!LookUpType:{}，请检查配置！", type);
        }
        return dbTag;
    }

    private String setDataSource (Object tenantId) {
        String dbTag = null;
        List<TenantBean> tenantBeans = DataSourceConfig.TENANTBEAN;
        for (TenantBean tenantBean : tenantBeans) {
            //先判断指定id数据源
            List<Long> idList = tenantBean.getSpecifyList();
            if (null != idList) {
                for (Long id : idList) {
                    if (id == Long.parseLong(tenantId.toString())) {
                        dbTag = tenantBean.getDbTag();
                        DynamicDataSourceHolder.putDataSource(dbTag);
                        return dbTag;
                    }
                }
            }
            //后判断区间id数据源
            if (Long.parseLong(tenantId.toString()) >= tenantBean.getStart() &&
                    Long.parseLong(tenantId.toString()) <= tenantBean.getEnd()) {
                dbTag = tenantBean.getDbTag();
                DynamicDataSourceHolder.putDataSource(dbTag);
                break;
            }
        }
        if (dbTag == null) {
            logger.error("数据库数据源无效！,key:{}，请检查配置！", tenantId);
        }
        return dbTag;
    }


    public void afterPropertiesSet11 () throws Exception {
        if (dynamicDataSource == null) {
            dynamicDataSource = SpringContextHolder.getBean(DynamicDataSource.class);
        }
        if (dynamicDataSource == null) {
            dynamicDataSource = new DynamicDataSource();
        }
        advisorDaoSupport = SpringContextHolder.getBean(SqlSessionDaoSupport.class);
        if (advisorDaoSupport == null) {
            SqlSessionFactory sessionFactory;
            sessionFactory = SpringContextHolder.getBean(SqlSessionFactory.class);
            if (sessionFactory == null) {
                SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
                fb.setDataSource(dynamicDataSource);// 指定数据源(这个必须有，否则报错)
                try {
                    fb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/**/*.xml"));
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    fb.setConfigLocation(new PathMatchingResourcePatternResolver().getResources("classpath*:mybatis.xml")[0]);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    sessionFactory = fb.getObject();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            advisorDaoSupport = new AdvisorDaoSupport();
            advisorDaoSupport.setSqlSessionFactory(sessionFactory);
        }
    }


    @Override
    public void setApplicationContext (ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void afterPropertiesSet () throws Exception {

        if (dynamicDataSource == null) {
            dynamicDataSource = getBean(DynamicDataSource.class);
        }
        if (dynamicDataSource == null) {
            dynamicDataSource = new DynamicDataSource();
        }
        if (dynamicDataSource.getResolvedDefaultDataSource() == null) {
            advisorDaoSupport = (AdvisorDaoSupport) getBean(SqlSessionDaoSupport.class);
        }
        if (advisorDaoSupport == null) {
            SqlSessionFactory sessionFactory=null;
            if (dynamicDataSource.getResolvedDefaultDataSource() == null)
            sessionFactory = getBean(SqlSessionFactory.class);
            if (sessionFactory == null) {
                SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
                fb.setDataSource(dynamicDataSource);// 指定数据源(这个必须有，否则报错)
                try {
                    fb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/**/*.xml"));
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    fb.setConfigLocation(new PathMatchingResourcePatternResolver().getResources("classpath*:mybatis.xml")[0]);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    sessionFactory = fb.getObject();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            advisorDaoSupport = new AdvisorDaoSupport();
            advisorDaoSupport.setSqlSessionFactory(sessionFactory);
        }
    }

    private <T> T getBean (Class<T> type) {
        try {
            return applicationContext == null ? null :
                    BeanFactoryUtils.beanOfTypeIncludingAncestors(applicationContext, type, false, false);
        } catch (Exception x) {
            logger.info("No injection configuration!");
        }
        return null;
    }
}
